// 引用与借用
fn main() {

    let mut msg: String = String::from("Hello Rust");
    // 引用即指针
    // test01(&msg);

    // test02(&mut msg);

    // 这行代码与 test02 组合也会引起编译器的 borrow-checker 报错
    // 可变引用被多个变量接收
    // let p1 = &mut msg;

    // test03();

    // test0302();

    // test04();

    test05();

}

// 不可变引用
fn test01(content: &String) {
    println!("{}", content);
}

// 可变引用
// 可变引用所指向的变量也应该是可变的
fn test02(content: &mut String) {
    content.push_str("_here");
    println!("{}", content);
}

// 不过可变引用并不是随心所欲、想用就用的，它有一个很大的限制： 同一作用域，特定数据只能有一个可变引用
fn test03() {
    let mut content = String::from("Hello Rust3");
    let p1 = &content;

    // 这里导致报错，因为 可变引用同时只能存在一个
    // let p2 = &mut content;
    // println!("{} {}", p1, p2);

}

/*
    在 Rust1.31 前，此方法不会编译通过，因为 p1 的作用域持续到方法结束
    在 1.31 之后，Rust 编译器进行了优化，p1 的作用域只持续到该变量最后一次使用位置（与 Java 优化类似）
    这种优化被 Rust 命名为 Non-Lexical Lifetimes(NLL)，专门用于找到某个引用在作用域(})结束前就不再被使用的代码位置
*/
fn test0302() {
    let mut content = String::from("Hello Rust3");
    let p1 = &content;
    println!("{}", p1);

    // 此时这里不会报错
    let p2 = &mut content;
    println!("{}", p2);

}

// 可变引用通过定义作用域可避开 borrow-checker
fn test04() {
    let mut msg = String::from("Hello Rust4");
    {
        let p1 = &mut msg;
    }
    let p2 = &mut msg;
}

/*
    悬垂引用也叫做悬垂指针，意思为指针指向某个值后，这个值被释放掉了，而指针仍然存在，其指向的内存可能不存在任何值或已被其它变量重新使用。
    在 Rust 中编译器可以确保引用永远也不会变成悬垂状态：
    当你获取数据的引用后，编译器可以确保数据不会在引用结束前被释放，要想释放数据，必须先停止其引用的使用
*/  
// fn dangle() -> &String {
//     let s = String::from("hello");
//     &s
// }

// 除了 & 还可以使用 ref 获取变量的引用
fn test05() {
    let msg = String::from("Hello Rust5");
    let ref p1 = msg;

    println!("{}", p1);

}
